<!--进度条-->
<template>
  <canvas
      :id="id"
      :width="width"
      :height="height"
      :style="`width:${width}px;height:${height}px`">
  </canvas>
</template>

<script>
const schema = {
  // 是否显示进度条方块之外的文本、指示器等元素。
  visible: true,
  itemStyle: {
    fontSize: 12,
  },
  data: [
    {
      name: '正常',
      value: 60,
      itemStyle: {
        color: '#04BD70',
      },
    },
    {
      name: '超标',
      value: 80,
      itemStyle: {
        color: '#FFB200',
      },
    },
    {
      name: '严重',
      value: 100,
      itemStyle: {
        color: '#E6392F',
      },
    },
  ],
};

/**
 * 获得当前色块的颜色
 * @param {number} value 当前进度值
 * @param {object[]} breakNodes 颜色分区列表，会根据object.value升序排列
 * @return {object} style 包含style.color
 */
function findCurrentRegionColor(value, breakNodes) {
  const defaultActiveColor = '#04BD70';
  breakNodes.sort((a, b) => (a.value - b.value));
  for (let i = 0; i < breakNodes.length; i++) {
    if (value <= breakNodes[i].value) {
      return {
        color: breakNodes[i].color || defaultActiveColor,
      };
    }
  }
  return {
    color: null,
  };
}

function paintText(ctx, textList) {
  const defaultTextColor = '#555';
  textList.forEach((textObj) => {
    const {
      name,
      nameX,
      nameY,
      value,
      valueX,
      valueY,
      style,
    } = textObj;
    ctx.fillStyle = style.color || defaultTextColor;
    ctx.font = `${style.fontSize || 10}px 微软雅黑,sans-serif`;
    ctx.fillText(name, nameX, nameY);
    ctx.fillText(value, valueX, valueY);
  });
}

function paintProgress(ctx, value, blockList, breakNodes) {
  const defaultColor = '#d8d8d8';
  const { color } = findCurrentRegionColor(value, breakNodes);
  blockList.forEach((block) => {
    const {
      value: blockValue,
      x,
      y,
      width,
      height,
    } = block;
    ctx.fillStyle = blockValue <= value ? color : defaultColor;
    ctx.moveTo(x, y);
    ctx.fillRect(x, y, width, height);
  });
}

function paintPointer(ctx, pointer) {
  const {
    x, y, height, color = '3F8CE1',
  } = pointer;
  ctx.fillStyle = color;
  ctx.beginPath();
  ctx.moveTo(x, y);
  ctx.lineTo(x + height / 2, y + height);
  ctx.lineTo(x - height / 2, y + height);
  ctx.fill();
}

/**
 * 执行一系列动画
 * @param {queue[]} queues item为一次执行中，某个执行步骤对应的帧数
 *    @param {array} queue 每个item代表某一帧
 * @param {function[]} handlers 一次执行中的n个步骤
 * @param {function} operator 一次执行中额外执行的函数
 */
function runAnimation(queues, handlers, operator) {
  if (!queues[0] || !queues[0].length) return;
  if (typeof operator === 'function') operator();
  for (let i = 0; i < handlers.length; i++) {
    const handler = handlers[i];
    // 当前执行步骤需要的帧数
    const queue = queues[i];
    // 当前需要执行的帧
    const params = queue[0];
    // 移除执行的帧
    queue.shift();
    handler(...params);
  }
  requestAnimationFrame(() => {
    runAnimation(queues, handlers, operator);
  });
}

export default {
  props: {
    // dom id,每个html页面必须唯一。
    id: {
      type: [Number, String],
      default: null,
    },
    // 组成进度条的方块数量
    length: {
      type: Number,
      default: 20,
    },
    // 用以表示的数值范围
    range: {
      type: Array,
      default() {
        return [0, 100];
      },
    },
    // 刻度值的精度（小数点后几位）
    precision: {
      type: Number,
      default: 0,
    },
    // 进度条当前值，默认为最小值
    value: {
      type: Number,
      default: null,
    },
    // 配置文本、指示器的样式属性。格式见schema。
    more: {
      type: Object,
      default() {
        return schema;
      },
    },
    // 画布尺寸
    width: {
      type: Number,
      default: 200,
    },
    height: {
      type: Number,
      default: 60,
    },
    // 进度条方块在canvas的边距；相关的文本、指示器都将在这个边距内绘制。
    grid: {
      type: Object,
      default() {
        return {
          left: 20,
          right: 20,
          top: 30,
          bottom: 20,
        };
      },
    },
    data: {
      type: Object,
      default() {
        return {
          normal: 10,
          excess: 80,
          value: 20
        }
      }
    }
  },
  data() {
    return {
      context: null,
    };
  },
  watch:{
    // value(){
    //   this.paint(this.context);
    // },
    data() {
      this.paint(this.context);
    }
  },
  mounted() {
    try {
      this.context = document.getElementById(this.id).getContext('2d');
    } catch (err) {
      throw Error(`初始化进度条失败，可能是未获得正确的id;Detail:${err.message}`);
    }

    this.paint(this.context);
  },
  methods: {
    paint(ctx) {
      const paddingLeft = this.grid.left;
      const paddingRight = this.grid.right;
      const paddingTop = this.grid.top;
      const paddingBototm = this.grid.bottom;
      // 方块间隔
      const gap = 2;
      // 绘制方块区域
      const w = this.width - paddingLeft - paddingRight;
      const h = this.height - paddingTop - paddingBototm;
      const perWidth = (w + gap) / this.length;
      // 方块左上方位置
      const startX = paddingLeft;
      const startY = this.height - paddingBototm - h;

      const min = this.range[0];
      const max = this.range[1];
      // eslint-disable-next-line
      const value = isNaN(this.value) ? min : this.value;
      // 每个方块代表的刻度值
      // eslint-disable-next-line
      const degree = Math.round((max - min) / this.length * Math.pow(10, this.precision)) / Math.pow(10, this.precision);
      const { data = [], itemStyle = {} } = this.more;
      // 分段节点数值
      const breakNodes = [];

      // const textList = data.map((item, index) => {
      //   const { name, itemStyle: substyle = {}, value: breakValue } = item;
      //   const style = Object.assign({}, itemStyle, substyle);
      //   const lastValue = (breakNodes.length && breakNodes[breakNodes.length - 1].value) || min;
      //   const distance = (breakValue - lastValue) / (max - min) * w;
      //   // console.error(distance)
      //   const valueX = (index + 1) * 45// ((breakValue - min) / (max - min)) * w + startX - ctx.measureText(`${breakValue}`).width / 2;
      //   const valueY = startY - 2;
      //   const nameX = valueX + ctx.measureText(`${breakValue}`).width / 2 - ctx.measureText(`${name}`).width / 2// - distance / 2;

      //   const nameY = valueY - style.fontSize;
      //   breakNodes.push({
      //     color: style.color, // 必须有该属性
      //     value: breakValue, // 必须有该属性
      //   });
      //   return {
      //     name,
      //     nameX,
      //     nameY,
      //     value: breakValue,
      //     valueX,
      //     valueY,
      //     style,
      //   };
      // });

      const textList = [{
        name: '正常',
        nameX: 27,
        nameY: 20,
        value: this.data.normal || '',
        valueX: 62 - (this.data.normal >= 100 ? 4 : 0),
        valueY: 20,
        style: {
          color: "#04BD70",
          fontSize: 12
        }
      }, {
        name: '超标',
        nameX: 88,
        nameY: 20,
        value: this.data.excess || '',
        valueX: 124 - (this.data.excess >= 100 ? 4 : 0),
        valueY: 20,
        style: {
          color: "#FFB200",
          fontSize: 12
        }
      }, {
        name: '严重',
        nameX: 150,
        nameY: 20,
        value: '',
        valueX: 40,
        valueY: 20,
        style: {
          color: "#E6392F",
          fontSize: 12
        }
      }]

      const blockList = Array.apply(null, { length: this.length }).map((block, i) => ({
        value: min + degree * (i + 1),
        x: startX + i * perWidth,
        y: startY,
        width: perWidth - gap,
        height: h,
      }));

      // 动画使用多少帧数来完成
      const animationFrames = 30;
      const splitValueQueue = Array.apply(null, { length: animationFrames })
        .map((a, i) => (value / animationFrames * (i + 1)));

      const textQueue = splitValueQueue.map(() => [
        ctx,
        textList,
      ]);

      const pointerQueue = splitValueQueue.map((splitValue) => {
        const pointer = {
          x: (splitValue - min) / (max - min) * w + startX,
          y: startY + h,
          height: paddingBototm,
          color: this.value <= 33.33 ? '#04BD70' : (this.value <= 66.66 ? '#FFB200' : '#E6392F')//findCurrentRegionColor(splitValue, breakNodes).color,
        };

        return [ctx, pointer];
      });

      const progressValueQueue = splitValueQueue.map(splitValue => [
        ctx,
        splitValue,
        blockList,
        breakNodes,
      ]);

      if (this.more.visible === true) {
        runAnimation(
          [progressValueQueue, textQueue, pointerQueue],
          [paintProgress, paintText, paintPointer],
          () => {
            ctx.clearRect(0, 0, this.width, this.height);
          },
        );
      } else {
        runAnimation(
          [progressValueQueue],
          [paintProgress],
          () => {
            ctx.clearRect(0, 0, this.width, this.height);
          },
        );
      }
    },
  },
};
</script>
<style  scoped>
  canvas {
    margin:auto;
  }
</style>
